import turtle as te
from bs4 import BeautifulSoup
import numpy as np
import time

# thanks to https://blog.csdn.net/liangwqi/article/details/103201455

line_buffer = []

WriteStep = 15  # 贝塞尔函数的取样次数
Height = 0
Width = 0
Xh = 0  # 记录前一个贝塞尔函数的手柄
Yh = 0
scale = (1, 1)
tf = (0, 0)
first = True

f = ''

class fake_te():
    def __init__(self) -> None:
        self.x = 0
        self.y = 0
    def penup(self):
        pass
    def pendown(self):
        pass
    def goto(self,x,y):
        self.x = x
        self.y = y
    def xcor(self):
        return self.x
    def ycor(self):
        return self.y
    def setup(self,*args):
        pass
    def setworldcoordinates(self, *args):
        pass
    def tracer(self,*args):
        pass
    def pensize(self,*args):
        pass
    def speed(self,*args):
        pass
    def color(self,*args):
        pass
    def hideturtle(self,*args):
        pass
    def update(self,*args):
        pass
    def done(self,*args):
        pass
    
# output
def slow_write(s): # 精简抬笔与落笔指令
    line_buffer.append(s)
    if len(line_buffer)>=2:
        if line_buffer[-1]==line_buffer[-2]:
            line_buffer.pop()
        elif line_buffer[-2:]==['u\n','d\n']:
                line_buffer.pop()
                line_buffer.pop()
    if len(line_buffer)>=3:
        f.write(line_buffer.pop(0))
    if s == 'end':
        for l in line_buffer:
            f.write(l)
    pass
def penup():
    te.penup()
    slow_write('u\n')
def pendown():
    te.pendown()
    slow_write('d\n')
def goto(x,y):
    te.goto(x,y)
    slow_write(f'{x},{y}\n')

# Bezier Implementation
def Bezier(p1, p2, t):  # 一阶贝塞尔函数
    return p1 * (1 - t) + p2 * t
def Bezier_2(x1, y1, x2, y2, x3, y3):  # 二阶贝塞尔函数
    goto(x1, y1)
    pendown()
    for t in range(0, WriteStep + 1):
        x = Bezier(Bezier(x1, x2, t / WriteStep),
                   Bezier(x2, x3, t / WriteStep), t / WriteStep)
        y = Bezier(Bezier(y1, y2, t / WriteStep),
                   Bezier(y2, y3, t / WriteStep), t / WriteStep)
        goto(x, y)
    penup()
def Bezier_3(x1, y1, x2, y2, x3, y3, x4, y4):  # 三阶贝塞尔函数
    x1 = -Width / 2 + x1
    y1 = Height / 2 - y1
    x2 = -Width / 2 + x2
    y2 = Height / 2 - y2
    x3 = -Width / 2 + x3
    y3 = Height / 2 - y3
    x4 = -Width / 2 + x4
    y4 = Height / 2 - y4  # 坐标变换
    goto(x1, y1)
    pendown()
    for t in range(0, WriteStep + 1):
        x = Bezier(Bezier(Bezier(x1, x2, t / WriteStep), Bezier(x2, x3, t / WriteStep), t / WriteStep),
                   Bezier(Bezier(x2, x3, t / WriteStep), Bezier(x3, x4, t / WriteStep), t / WriteStep), t / WriteStep)
        y = Bezier(Bezier(Bezier(y1, y2, t / WriteStep), Bezier(y2, y3, t / WriteStep), t / WriteStep),
                   Bezier(Bezier(y2, y3, t / WriteStep), Bezier(y3, y4, t / WriteStep), t / WriteStep), t / WriteStep)
        goto(x, y)
    penup()

# Turtle Control
def Moveto(x, y):  # 移动到svg坐标下（x，y）
    penup()
    goto(-Width / 2 + x, Height / 2 - y)
    pendown()
def Moveto_r(dx, dy):
    penup()
    goto(te.xcor() + dx, te.ycor() - dy)
    pendown()
def line(x1, y1, x2, y2):  # 连接svg坐标下两点
    penup()
    goto(-Width / 2 + x1, Height / 2 - y1)
    pendown()
    goto(-Width / 2 + x2, Height / 2 - y2)
    penup()
def Lineto_r(dx, dy):  # 连接当前点和相对坐标（dx，dy）的点
    pendown()
    goto(te.xcor() + dx, te.ycor() - dy)
    penup()
def Lineto(x, y):  # 连接当前点和svg坐标下（x，y）
    pendown()
    goto(-Width / 2 + x, Height / 2 - y)
    penup()
def Curveto(x1, y1, x2, y2, x, y):  # 三阶贝塞尔曲线到（x，y）
    penup()
    X_now = te.xcor() + Width / 2
    Y_now = Height / 2 - te.ycor()
    Bezier_3(X_now, Y_now, x1, y1, x2, y2, x, y)
    global Xh
    global Yh
    Xh = x - x2
    Yh = y - y2
def Curveto_r(x1, y1, x2, y2, x, y):  # 三阶贝塞尔曲线到相对坐标（x，y）
    penup()
    X_now = te.xcor() + Width / 2
    Y_now = Height / 2 - te.ycor()
    Bezier_3(X_now, Y_now, X_now + x1, Y_now + y1,
             X_now + x2, Y_now + y2, X_now + x, Y_now + y)
    global Xh
    global Yh
    Xh = x - x2
    Yh = y - y2
 
# read SVG attribute
def transform(w_attr):
    funcs = w_attr.split(' ')
    for func in funcs:
        func_name = func[0: func.find('(')]
        if func_name == 'scale':
            global scale
            scale = (float(func[func.find('(') + 1: -1].split(',')[0]),
                    -float(func[func.find('(') + 1: -1].split(',')[1]))
        if func_name == "translate":
            global tf
            tf = (float(func[func.find('(') + 1: -1].split(',')[0]),
                 -float(func[func.find('(') + 1: -1].split(',')[1]))
# svg prase
def readPathAttrD(w_attr):
    ulist = w_attr.split(' ')
    while '' in ulist:
        ulist.remove('')
    for i in ulist:
        if len(i) == 0: continue
        # print("now cmd:", i)
        if i.isdigit():
            yield float(i)
        elif i[0].isalpha():
            yield i[0]
        else: yield float(i)
            #yield float(i[1:])
# convert svg to turtle control
def drawSVG(filename, w_color, quick_finish = False):
    global Height,Width,line_buffer
    line_buffer = []
    SVGFile = open(filename, 'r')
    SVG = BeautifulSoup(SVGFile.read(), features='xml',)
    Height = float(SVG.svg.attrs['height'][0: -2])
    Width = float(SVG.svg.attrs['width'][0: -2])
    #transform(SVG.g.attrs['transform'])
    if first:
        te.setup(Width, Height, 0,0)
        te.setworldcoordinates( Width / 2,  Height / 2,
                               -Width / 2, -Height / 2)
    
    #te.goto(0,0)
    #te.goto(100,0)
    #te.goto(0,100)
    
    if quick_finish:
        te.tracer(3)
    #te.delay(1000)
    te.pensize(1)
    te.speed(1)
    penup()
    te.color(w_color)
 
    for i in SVG.find_all('path'):
        if i.parent.attrs['id'] == 'patch_1': continue
        attr = i.attrs['d'].replace('\n', ' ')
        f = readPathAttrD(attr)
        lastI = ''
        for i in f:
            if i == 'z':
                pass
            elif i == 'M':
                #te.end_fill()
                Moveto(next(f) * scale[0], next(f) * scale[1])
                #te.begin_fill()
            elif i == 'm':
                #te.end_fill()
                Moveto_r(next(f) * scale[0], next(f) * scale[1])
                #te.begin_fill()
            elif i == 'C':
                Curveto(next(f) * scale[0], next(f) * scale[1],
                        next(f) * scale[0], next(f) * scale[1],
                        next(f) * scale[0], next(f) * scale[1])
                lastI = i
            elif i == 'c':
                Curveto_r(next(f) * scale[0], next(f) * scale[1],
                          next(f) * scale[0], next(f) * scale[1],
                          next(f) * scale[0], next(f) * scale[1])
                lastI = i
            elif i == 'L':
                Lineto(next(f) * scale[0], next(f) * scale[1])
            elif i == 'l':
                Lineto_r(next(f) * scale[0], next(f) * scale[1])
                lastI = i
            elif lastI == 'C':
                Curveto(i * scale[0], next(f) * scale[1],
                        next(f) * scale[0], next(f) * scale[1],
                        next(f) * scale[0], next(f) * scale[1])
            elif lastI == 'c':
                Curveto_r(i * scale[0], next(f) * scale[1],
                          next(f) * scale[0], next(f) * scale[1],
                          next(f) * scale[0], next(f) * scale[1])
            elif lastI == 'L':
                Lineto(i * scale[0], next(f) * scale[1])
            elif lastI == 'l':
                Lineto_r(i * scale[0], next(f) * scale[1])
    penup()
    te.hideturtle()
    te.update()
    SVGFile.close()
    
def drawin_create(svgpath,drawinpath='./src/command.drawin',no_te = False, quick_finish = True):
    global f
    global te
    if no_te:
        te = fake_te()
    start = time.time()
    f = open(drawinpath,'w+')
    drawSVG(svgpath,'#000000',quick_finish)
    slow_write('end')
    f.close()
    end = time.time()
    print(f'Finished. Used time:{end-start}s')
    te.done()
if __name__ == '__main__':
    drawin_create('./img_canny.svg','with_te.drawin',no_te=False,quick_finish=True)
import turtle as te
from bs4 import BeautifulSoup
import numpy as np
import time

# thanks to https://blog.csdn.net/liangwqi/article/details/103201455

line_buffer = []

WriteStep = 15  # 贝塞尔函数的取样次数
Height = 0
Width = 0
Xh = 0  # 记录前一个贝塞尔函数的手柄
Yh = 0
scale = (1.2, 1.2)
tf = (0, 0)
first = True

f = ''

class fake_te():
    def __init__(self) -> None:
        self.x = 0
        self.y = 0
    def penup(self):
        pass
    def pendown(self):
        pass
    def goto(self,x,y):
        self.x = x
        self.y = y
    def xcor(self):
        return self.x
    def ycor(self):
        return self.y
    def setup(self,*args):
        pass
    def setworldcoordinates(self, *args):
        pass
    def tracer(self,*args):
        pass
    def pensize(self,*args):
        pass
    def speed(self,*args):
        pass
    def color(self,*args):
        pass
    def hideturtle(self,*args):
        pass
    def update(self,*args):
        pass
    def done(self,*args):
        pass
    
# output
def slow_write(s): # 精简抬笔与落笔指令
    line_buffer.append(s)
    if len(line_buffer)>=2:
        if line_buffer[-1]==line_buffer[-2]:
            line_buffer.pop()
        elif line_buffer[-2:]==['u\n','d\n']:
                line_buffer.pop()
                line_buffer.pop()
    if len(line_buffer)>=3:
        f.write(line_buffer.pop(0))
    if s == 'end':
        for l in line_buffer:
            f.write(l)
        line_buffer.clear()
    pass
def penup():
    te.penup()
    slow_write('u\n')
def pendown():
    te.pendown()
    slow_write('d\n')
def goto(x,y):
    te.goto(x,y)
    slow_write(f'{x},{y}\n')

# Bezier Implementation
def Bezier(p1, p2, t):  # 一阶贝塞尔函数
    return p1 * (1 - t) + p2 * t
def Bezier_2(x1, y1, x2, y2, x3, y3):  # 二阶贝塞尔函数
    goto(x1, y1)
    pendown()
    for t in range(0, WriteStep + 1):
        x = Bezier(Bezier(x1, x2, t / WriteStep),
                   Bezier(x2, x3, t / WriteStep), t / WriteStep)
        y = Bezier(Bezier(y1, y2, t / WriteStep),
                   Bezier(y2, y3, t / WriteStep), t / WriteStep)
        goto(x, y)
    penup()
def Bezier_3(x1, y1, x2, y2, x3, y3, x4, y4):  # 三阶贝塞尔函数
    x1 = -Width / 2 + x1
    y1 = Height / 2 - y1
    x2 = -Width / 2 + x2
    y2 = Height / 2 - y2
    x3 = -Width / 2 + x3
    y3 = Height / 2 - y3
    x4 = -Width / 2 + x4
    y4 = Height / 2 - y4  # 坐标变换
    goto(x1, y1)
    pendown()
    for t in range(0, WriteStep + 1):
        x = Bezier(Bezier(Bezier(x1, x2, t / WriteStep), Bezier(x2, x3, t / WriteStep), t / WriteStep),
                   Bezier(Bezier(x2, x3, t / WriteStep), Bezier(x3, x4, t / WriteStep), t / WriteStep), t / WriteStep)
        y = Bezier(Bezier(Bezier(y1, y2, t / WriteStep), Bezier(y2, y3, t / WriteStep), t / WriteStep),
                   Bezier(Bezier(y2, y3, t / WriteStep), Bezier(y3, y4, t / WriteStep), t / WriteStep), t / WriteStep)
        goto(x, y)
    penup()

# Turtle Control
def Moveto(x, y):  # 移动到svg坐标下（x，y）
    penup()
    goto(-Width / 2 + x, Height / 2 - y)
    pendown()
def Moveto_r(dx, dy):
    penup()
    goto(te.xcor() + dx, te.ycor() - dy)
    pendown()
def line(x1, y1, x2, y2):  # 连接svg坐标下两点
    penup()
    goto(-Width / 2 + x1, Height / 2 - y1)
    pendown()
    goto(-Width / 2 + x2, Height / 2 - y2)
    penup()
def Lineto_r(dx, dy):  # 连接当前点和相对坐标（dx，dy）的点
    pendown()
    goto(te.xcor() + dx, te.ycor() - dy)
    penup()
def Lineto(x, y):  # 连接当前点和svg坐标下（x，y）
    pendown()
    goto(-Width / 2 + x, Height / 2 - y)
    penup()
def Curveto(x1, y1, x2, y2, x, y):  # 三阶贝塞尔曲线到（x，y）
    penup()
    X_now = te.xcor() + Width / 2
    Y_now = Height / 2 - te.ycor()
    Bezier_3(X_now, Y_now, x1, y1, x2, y2, x, y)
    global Xh
    global Yh
    Xh = x - x2
    Yh = y - y2
def Curveto_r(x1, y1, x2, y2, x, y):  # 三阶贝塞尔曲线到相对坐标（x，y）
    penup()
    X_now = te.xcor() + Width / 2
    Y_now = Height / 2 - te.ycor()
    Bezier_3(X_now, Y_now, X_now + x1, Y_now + y1,
             X_now + x2, Y_now + y2, X_now + x, Y_now + y)
    global Xh
    global Yh
    Xh = x - x2
    Yh = y - y2
 
# read SVG attribute
def transform(w_attr):
    funcs = w_attr.split(' ')
    for func in funcs:
        func_name = func[0: func.find('(')]
        if func_name == 'scale':
            global scale
            scale = (float(func[func.find('(') + 1: -1].split(',')[0]),
                    -float(func[func.find('(') + 1: -1].split(',')[1]))
        if func_name == "translate":
            global tf
            tf = (float(func[func.find('(') + 1: -1].split(',')[0]),
                 -float(func[func.find('(') + 1: -1].split(',')[1]))
# svg prase
def readPathAttrD(w_attr):
    ulist = w_attr.split(' ')
    while '' in ulist:
        ulist.remove('')
    for i in ulist:
        if len(i) == 0: continue
        # print("now cmd:", i)
        if i.isdigit():
            yield float(i)
        elif i[0].isalpha():
            yield i[0]
        else: yield float(i)
            #yield float(i[1:])
# convert svg to turtle control
def drawSVG(filename, w_color, quick_finish = False):
    global Height,Width
    SVGFile = open(filename, 'r')
    SVG = BeautifulSoup(SVGFile.read(), features='xml',)
    Height = float(SVG.svg.attrs['height'][0: -2])
    Width = float(SVG.svg.attrs['width'][0: -2])
    #transform(SVG.g.attrs['transform'])
    if first:
        te.setup(Width, Height, 0,0)
        te.setworldcoordinates( Width / 2,  Height / 2,
                               -Width / 2, -Height / 2)
    
    #te.goto(0,0)
    #te.goto(100,0)
    #te.goto(0,100)
    
    if quick_finish:
        te.tracer(3)
    #te.delay(1000)
    te.pensize(1)
    te.speed(1)
    penup()
    te.color(w_color)
 
    for i in SVG.find_all('path'):
        if i.parent.attrs['id'] == 'patch_1': continue
        attr = i.attrs['d'].replace('\n', ' ')
        f = readPathAttrD(attr)
        lastI = ''
        for i in f:
            if i == 'z':
                pass
            elif i == 'M':
                #te.end_fill()
                Moveto(next(f) * scale[0], next(f) * scale[1])
                #te.begin_fill()
            elif i == 'm':
                #te.end_fill()
                Moveto_r(next(f) * scale[0], next(f) * scale[1])
                #te.begin_fill()
            elif i == 'C':
                Curveto(next(f) * scale[0], next(f) * scale[1],
                        next(f) * scale[0], next(f) * scale[1],
                        next(f) * scale[0], next(f) * scale[1])
                lastI = i
            elif i == 'c':
                Curveto_r(next(f) * scale[0], next(f) * scale[1],
                          next(f) * scale[0], next(f) * scale[1],
                          next(f) * scale[0], next(f) * scale[1])
                lastI = i
            elif i == 'L':
                Lineto(next(f) * scale[0], next(f) * scale[1])
            elif i == 'l':
                Lineto_r(next(f) * scale[0], next(f) * scale[1])
                lastI = i
            elif lastI == 'C':
                Curveto(i * scale[0], next(f) * scale[1],
                        next(f) * scale[0], next(f) * scale[1],
                        next(f) * scale[0], next(f) * scale[1])
            elif lastI == 'c':
                Curveto_r(i * scale[0], next(f) * scale[1],
                          next(f) * scale[0], next(f) * scale[1],
                          next(f) * scale[0], next(f) * scale[1])
            elif lastI == 'L':
                Lineto(i * scale[0], next(f) * scale[1])
            elif lastI == 'l':
                Lineto_r(i * scale[0], next(f) * scale[1])
    penup()
    te.hideturtle()
    te.update()
    SVGFile.close()
    
def drawin_create(svgpath,drawinpath='./src/command.drawin',no_te = False, quick_finish = True):
    global f
    global te
    if no_te:
        te = fake_te()
    start = time.time()
    f = open(drawinpath,'w+')
    drawSVG(svgpath,'#000000',quick_finish)
    slow_write('end')
    f.close()
    end = time.time()
    print(f'Finished. Used time:{end-start}s')
    te.done()
if __name__ == '__main__':
    drawin_create('img_canny.svg',no_te=True)
    # drawin_create('./img_canny.svg','with_te.drawin',no_te=True,quick_finish=True)
    # f = open('command.drawin','w+')
    # slow_write('u\n')
    # slow_write('502\n')
    # slow_write('d\n')
    # slow_write('503\n')
    # slow_write('504\n')
    # slow_write('502\n')
    # slow_write('end')
    # f.close()

